package edu.westga.cs.recommender.model;

import java.util.LinkedList;

import edu.westga.cs.recommender.model.datatypes.*;
import edu.westga.cs.recommender.model.datatypes.limited.Match;
import edu.westga.cs.recommender.interfaces.RetrieveItemMatches;

/**
 * Gets all the items matching the tags for a given profile.
 * 
 * DO NOT MESS WITH THIS CLASS - the code is very easily broken, and doesn't need to change
 * to accommodate customization elsewhere.
 * @author Brent W. Ezell
 *
 */
public class RetrieveMatches {
	//Fields
	private RetrieveItemMatches info;
	private LinkedList<String> tags;
	private LinkedList<String> itemNames;
	private LinkedList<String> interimNames;
	private LinkedList<Match> matches;
	private LinkedList<Item> items;
	
	//Constructor
	/**
	 * Initializes the class.
	 */
	public RetrieveMatches(RetrieveInfo info) {
		this.info = info;
		this.items = new LinkedList<Item>();
		this.matches = new LinkedList<Match>();
		this.interimNames = new LinkedList<String>();
	}
	
	//Methods
	/**
	 * Uses private helper methods to return items matching a given profile's tags.
	 * @param profile The profile to retrieve against.
	 * @return A list of items matching the tags for the given profile.
	 */
	public LinkedList<Item> getItems(Profile profile) {
		this.getTags(profile);
		this.getMatches();
		this.constrainMatches();
		int numMatches = this.itemNames.size();
		String currentItemName;
		Item currentItem;
		for (int n = 0; n < numMatches; n++) {
			currentItemName = this.itemNames.get(n);
			currentItem = this.info.retrieveItem(currentItemName);
			this.items.add(currentItem);
		}
		return this.items;
	}
	
	/**
	 * Helper method for getItems.
	 * Gets the tags for a specified profile.
	 * @param profile Profile to retrieve tags for.
	 */
	private void getTags(Profile profile) {
		this.tags = profile.getTags();
	}
	
	/**
	 * Helper method for getItems.
	 * Gets the items matching the tags and calls checkName to handle them, one tag at a time.
	 */
	private void getMatches() {
		int num = this.tags.size();
		String currentTag = "";
		for (int n = 0; n < num; n++) {
			currentTag = this.tags.get(n);
			this.interimNames = this.info.retrieveMatchingItemNames(currentTag);
			this.checkName();
		}
	}
	
	/**
	 * Helper method for getMatches.
	 * Takes a specified set of item names and figures out whether the items have been retrieved
	 * before or not.
	 * If they have, their listing in this.matches has its hit iterated, and if not, a new item is added
	 * to this.matches to hold them. 
	 * Evil kludge, but functional - mess with this method at your own peril!
	 */
	private void checkName() {
		int sizeInterim = this.interimNames.size();
		int sizeMatches = this.matches.size();
		Match add;
		if (sizeMatches == 0) {
			for (int l = 0; l < sizeInterim; l++) {
				add = new Match(this.interimNames.get(l));
				this.matches.add(add);
			}
			return;
		}
		String interimName;
		String interimMatch;
		for (int n = 0; n < sizeInterim; n++) {
			for (int m = 0; m < sizeMatches; m++) {
				interimName = this.interimNames.get(n);
				interimMatch = this.matches.get(m).getName();
				if (interimName.equals(interimMatch)) {
					this.matches.get(m).addHit();
				}
				else {
					add = new Match(this.interimNames.get(n));
					this.matches.add(add);
				}
			}
		}
	}
	
	/**
	 * Takes this.matches and analyzes it to get a list of item names matching all tags provided via
	 * the number of hits recorded in the Match data type.
	 */
	private void constrainMatches() {
		int numTags = this.tags.size();
		int hits = 0;
		int numCandidates = this.matches.size();
		LinkedList<String> finalMatches = new LinkedList<String>();
		for (int n = 0; n < numCandidates; n++) {
			hits = this.matches.get(n).getHits();
			if (hits == numTags) {
				finalMatches.add(this.matches.get(n).getName());
			}
		}
		this.itemNames = finalMatches;
	}
}
